package tweeks.user.domain;

import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import org.springframework.security.core.CredentialsContainer;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.Assert;

import tweeks.common.domain.UUIDEntity;

public class User extends UUIDEntity implements UserDetails, CredentialsContainer {
	
	private static final long serialVersionUID = 3929784118106805082L;

	private String loginId;
	
	private String email;
	
	private String password;
	
	private String firstName;

	private String lastName;
	
	private boolean accountNonExpired;
	
    private boolean accountNonLocked;
    
    private boolean credentialsNonExpired;
    
    private boolean enabled;
	
	private Set<GrantedAuthority> authorities;
	
	private Group group = new Group();
	
	private Permission permission = new Permission();
	
	public User() {
		
	}
	
	public User(String loginId, String email, String password,
			String firstName, String lastName) {
		this.loginId = loginId;
		this.email = email;
		this.password = password;
		this.firstName = firstName;
		this.lastName = lastName;
	}
	
	public User(String loginId, String email, String password,
			String firstName, String lastName, boolean accountNonExpired,
			boolean accountNonLocked, boolean credentialsNonExpired,
			boolean enabled, Collection<? extends GrantedAuthority> authorities) {
		super();
		this.loginId = loginId;
		this.email = email;
		this.password = password;
		this.firstName = firstName;
		this.lastName = lastName;
		this.accountNonExpired = accountNonExpired;
		this.accountNonLocked = accountNonLocked;
		this.credentialsNonExpired = credentialsNonExpired;
		this.enabled = enabled;
		this.authorities = Collections.unmodifiableSet(sortAuthorities(authorities));
	}

	public String getLoginId() {
		return loginId;
	}

	public void setLoginId(String loginId) {
		this.loginId = loginId;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	
	public String getGroupId() {
		return this.group.getId();
	}
	
	public void setGroupId(String groupId) {
		this.group.setId(groupId);
	}
	
	public String getGroupCode() {
		return this.group.getCode();
	}
	
	public void setGroupCode(String groupCode) {
		this.group.setCode(groupCode);
	}
	
	public String getGroupName() {
		return this.group.getName();
	}
	
	public void setGroupName(String groupName) {
		this.group.setName(groupName);
	}
	
	public String getPermissionId() {
		return this.permission.getId();
	}
	
	public void setPermissionId(String permissionId) {
		this.permission.setId(permissionId);
	}
	
	public String getPermissionCode() {
		return this.permission.getCode();
	}
	
	public void setPermissionCode(String PermissionCode) {
		this.permission.setCode(PermissionCode);
	}
	
	public String getPermissionName() {
		return this.permission.getName();
	}
	
	public void setPermissionName(String PermissionName) {
		this.permission.setName(PermissionName);
	}

	@Override
	public void eraseCredentials() {
		password = null;
	}

	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		return authorities;
	}

	@Override
	public String getUsername() {
		return loginId;
	}

	@Override
	public boolean isAccountNonExpired() {
		return accountNonExpired;
	}

	@Override
	public boolean isAccountNonLocked() {
		return accountNonLocked;
	}

	@Override
	public boolean isCredentialsNonExpired() {
		return credentialsNonExpired;
	}

	@Override
	public boolean isEnabled() {
		return enabled;
	}

	private static SortedSet<GrantedAuthority> sortAuthorities(Collection<? extends GrantedAuthority> authorities) {
        Assert.notNull(authorities, "Cannot pass a null GrantedAuthority collection");
        // Ensure array iteration order is predictable (as per UserDetails.getAuthorities() contract and SEC-717)
        SortedSet<GrantedAuthority> sortedAuthorities =
            new TreeSet<GrantedAuthority>(new AuthorityComparator());

        for (GrantedAuthority grantedAuthority : authorities) {
            Assert.notNull(grantedAuthority, "GrantedAuthority list cannot contain any null elements");
            sortedAuthorities.add(grantedAuthority);
        }

        return sortedAuthorities;
    }
	
	private static class AuthorityComparator implements Comparator<GrantedAuthority>, Serializable {
        private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

        public int compare(GrantedAuthority g1, GrantedAuthority g2) {
            // Neither should ever be null as each entry is checked before adding it to the set.
            // If the authority is null, it is a custom authority and should precede others.
            if (g2.getAuthority() == null) {
                return -1;
            }

            if (g1.getAuthority() == null) {
                return 1;
            }

            return g1.getAuthority().compareTo(g2.getAuthority());
        }
    }
}
